package com.docmgmt.mvp.ragflow;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * RAGFlow用户认证客户端
 * 负责RAGFlow Web UI API的会话管理和身份验证
 *
 * RAGFlow有两套认证系统：
 * 1. API Key认证 - 用于 /api/v1/* SDK端点
 * 2. Session Token认证 - 用于 /v1/* Web UI端点
 *
 * 本客户端实现Session Token认证，用于查询LLM列表等Web UI功能
 */
@Slf4j
@Component
public class RAGFlowAuthClient {

    @Value("${ragflow.base-url}")
    private String apiUrl;

    @Value("${ragflow.admin.email:admin@example.com}")
    private String adminEmail;

    @Value("${ragflow.admin.password:admin}")
    private String adminPassword;

    private final HttpClient httpClient;
    private final ObjectMapper objectMapper;

    // 缓存的会话令牌（JWT序列化的access_token，用于Authorization header）
    private String cachedSessionToken;

    // 令牌过期时间
    private LocalDateTime tokenExpiry;

    // 令牌有效期（2小时，与RAGFlow默认会话时长匹配）
    private static final Duration TOKEN_VALIDITY = Duration.ofHours(2);

    public RAGFlowAuthClient() {
        this.httpClient = HttpClient.newBuilder()
                .connectTimeout(Duration.ofSeconds(10))
                .build();
        this.objectMapper = new ObjectMapper();
    }

    /**
     * 获取会话令牌（带缓存和自动刷新）
     *
     * @return 会话令牌
     * @throws Exception 如果登录失败
     */
    public String getSessionToken() throws Exception {
        // 检查缓存的令牌是否有效
        if (cachedSessionToken != null && tokenExpiry != null
                && LocalDateTime.now().isBefore(tokenExpiry)) {
            log.debug("使用缓存的会话令牌");
            return cachedSessionToken;
        }

        // 令牌过期或不存在，重新登录
        log.info("会话令牌已过期或不存在，开始登录RAGFlow...");
        return refreshSessionToken();
    }

    /**
     * 刷新会话令牌（执行登录）
     *
     * @return 新的会话令牌
     * @throws Exception 如果登录失败
     */
    public synchronized String refreshSessionToken() throws Exception {
        // 构造登录请求
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("email", adminEmail);

        // 使用RSA加密密码（RAGFlow要求密码必须加密传输）
        String encryptedPassword = RAGFlowPasswordEncryptor.encryptPassword(adminPassword);
        requestBody.put("password", encryptedPassword);

        log.debug("密码已RSA加密，长度: {}", encryptedPassword.length());

        String requestJson = objectMapper.writeValueAsString(requestBody);

        // 发送登录请求到 /v1/user/login
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(apiUrl + "/v1/user/login"))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(requestJson))
                .timeout(Duration.ofSeconds(30))
                .build();

        log.debug("发送RAGFlow登录请求: POST {}/v1/user/login, email={}", apiUrl, adminEmail);

        HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

        if (response.statusCode() != 200) {
            log.error("RAGFlow登录失败: HTTP {}, 响应: {}", response.statusCode(), response.body());
            throw new RuntimeException("RAGFlow登录失败: HTTP " + response.statusCode() + ", " + response.body());
        }

        // 先解析为Map检查code
        @SuppressWarnings("unchecked")
        Map<String, Object> responseMap = objectMapper.readValue(response.body(), Map.class);

        Integer code = (Integer) responseMap.get("code");
        String message = (String) responseMap.get("message");

        if (code == null || code != 0) {
            log.error("RAGFlow登录失败: code={}, message={}", code, message);
            throw new RuntimeException("RAGFlow登录失败: " + message);
        }

        // 提取Authorization header（JWT序列化的access_token）
        // RAGFlow的User.get_id()方法返回: jwt.dumps(str(access_token))
        String sessionToken = response.headers().firstValue("Authorization").orElse(null);

        if (sessionToken == null || sessionToken.isEmpty()) {
            log.error("RAGFlow登录响应中没有Authorization header: headers={}", response.headers().map());
            throw new RuntimeException("RAGFlow登录响应中没有Authorization header");
        }

        log.debug("成功获取RAGFlow Authorization token，长度: {}", sessionToken.length());

        // 缓存令牌
        this.cachedSessionToken = sessionToken;
        this.tokenExpiry = LocalDateTime.now().plus(TOKEN_VALIDITY);

        log.info("RAGFlow登录成功，会话令牌已缓存，过期时间: {}", tokenExpiry);

        return sessionToken;
    }

    /**
     * 清除缓存的会话令牌（当收到401响应时调用）
     */
    public void clearSessionToken() {
        log.info("清除缓存的会话令牌");
        this.cachedSessionToken = null;
        this.tokenExpiry = null;
    }

    /**
     * 检查会话令牌是否有效
     *
     * @return true如果令牌有效
     */
    public boolean isSessionValid() {
        return cachedSessionToken != null && tokenExpiry != null
                && LocalDateTime.now().isBefore(tokenExpiry);
    }

    /**
     * 获取当前缓存的令牌（不刷新）
     *
     * @return 当前令牌，如果没有则返回null
     */
    public String getCachedToken() {
        return cachedSessionToken;
    }

    // ==================== 内部类：响应结构 ====================

    /**
     * RAGFlow登录响应结构
     */
    @Data
    private static class LoginResponse {
        private Integer code;
        private String message;
        private LoginData data;
    }

    /**
     * 登录响应数据部分
     */
    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    private static class LoginData {
        /**
         * 会话令牌（access_token字段）
         */
        @JsonProperty("access_token")
        private String accessToken;

        /**
         * 用户ID
         */
        @JsonProperty("user_id")
        private String userId;

        /**
         * 用户昵称
         */
        private String nickname;

        /**
         * 用户邮箱
         */
        private String email;
    }
}
